/* omega copyright (C) by Laurence Raphael Brothers, 1987,1988,1989 */
/* inv.c */
/* functions having to do with player item inventory */

#ifdef MSDOS
# include "curses.h"
#else
# ifdef AMIGA
#  include <curses210.h>
# else
#  include <curses.h>
# endif
#endif

#include "glob.h"

/* drops money, heh heh */
void drop_money()
{
  pob money;

  money = detach_money();
  if (money != NULL) {
    if (Current_Environment == E_CITY) {
      print1("As soon as the money leaves your hand,");
      print2("a horde of scrofulous beggars snatch it up and are gone!");
    }
    else drop_at(Player.x,Player.y,money);
  }
  else setgamestatus(SKIP_MONSTERS);
}



/* returns some money from player back into "money" item.
   for giving and dropping money */
pob detach_money()
{
  long c;
  pob cash=NULL;
  c = get_money(Player.cash);
  if (c != ABORT) {
    Player.cash -= c;
    cash = ((pob) checkmalloc(sizeof(objtype)));
    make_cash(cash,difficulty());
    cash->basevalue = c;
  }
  return(cash);
}


/* gets a legal amount of money or ABORT */
long get_money(limit)
long limit;
{
  long c;
  print1("How much? ");
  c = parsenum();
  if (c > limit) {
    print3("Forget it, buddy.");
    return(ABORT);
  }
  else return(c);
}



/* pick up from some location x,y */
/* Lift entire itemlist off ground, pass it to inventory control, which
   may drop things back onto the now null ground */
void pickup_at(x,y)
int x,y;
{
  int quit = FALSE;
  char response;
  pol ol = Level->site[x][y].things;
  pol temp;

  resetgamestatus(FAST_MOVE);

  Level->site[x][y].things = NULL;

  while (ol != NULL) {
    if (! quit) {
      clearmsg1();
      print1("Pick up: ");
      nprint1(itemid(ol->thing));
      nprint1(" [ynq]: ");
      response = ynq1();
      quit = (response == 'q');
    }
    if (response == 'y') gain_item(ol->thing);
    else drop_at(x,y,ol->thing);
    temp = ol;
    ol = ol->next;
    temp->thing = NULL;
    temp->next = NULL;
    free((char *) temp);
  }
}


/* put all of o on objlist at x,y on Level->depth */
/* Not necessarily dropped by character; just dropped... */
void drop_at(x,y,o)
int x,y;
pob o;
{
  pol tmp;
  pob cpy; 
  
  if (Current_Environment != E_COUNTRYSIDE) {
    if ((Level->site[x][y].locchar != VOID_CHAR) &&
	(Level->site[x][y].locchar != ABYSS)) {
      cpy = ((pob) checkmalloc(sizeof(objtype)));
      tmp = ((pol) checkmalloc(sizeof(oltype)));
      *cpy = *o;
      cpy->used = FALSE;
      tmp->thing = cpy;
      tmp->next = Level->site[x][y].things;
      Level->site[x][y].things = tmp;
    }
    else if (Level->site[x][y].p_locf == L_VOID_STATION)
      setgamestatus(PREPARED_VOID);
  }
}  

/* put n of o on objlist at x,y on Level->depth */
void p_drop_at(x,y,n,o)
int x,y;
int n;
pob o;
{
  pol tmp; 
  if (Current_Environment != E_COUNTRYSIDE) 
    if ((Level->site[x][y].locchar != VOID_CHAR) &&
	(Level->site[x][y].locchar != ABYSS)) {
      tmp = ((pol) checkmalloc(sizeof(oltype)));
      tmp->thing = ((pob) checkmalloc(sizeof(objtype)));
      *(tmp->thing) = *o;
      tmp->thing->used = FALSE;
      tmp->thing->number = n;
      print2("Dropped ");
      nprint2(itemid(tmp->thing));
      morewait();
      tmp->next = Level->site[x][y].things;
      Level->site[x][y].things = tmp;
    }
    else if (Level->site[x][y].p_locf == L_VOID_STATION)
      setgamestatus(PREPARED_VOID);
}  


/* returns a string for identified items */
char *itemid(obj)
pob obj;
{
  char tstr[80];
  if (obj->objchar==CASH){
    strcpy(Str4,obj->truename);
    return(Str4);
  }
  else {
    if (Objects[obj->id].known > obj->known)
      obj->known = Objects[obj->id].known;
    
    setnumstr(obj,tstr);
    strcpy(Str4,tstr);
    if (obj->known == 0)
      strcat(Str4,obj->objstr);
    else if (obj->known == 1) {
      if (obj->id == ARTIFACTID + 8 || obj->id == ARTIFACTID + 20 ||
	obj->id == ARTIFACTID + 21)
	strcat(Str4, "the ");
      strcat(Str4,obj->truename);
    }
    else {
      if (obj->id == ARTIFACTID + 8 || obj->id == ARTIFACTID + 20 ||
	obj->id == ARTIFACTID + 21)
	strcat(Str4, "the ");
      if (obj->usef == I_NOTHING && Objects[obj->id].usef != I_NOTHING)
	strcat(Str4, "disenchanted ");
      if (obj->blessing < 0) {
	strcat(Str4, "cursed ");
	strcat(Str4, obj->cursestr);
      }
      else if (obj->blessing > 0) {
	strcat(Str4, "blessed ");
	strcat(Str4, obj->truename);
      }
      else strcat(Str4,obj->truename);
      if (obj->number > 1) strcat(Str4,"s");
      switch (obj->objchar) {
      case STICK: 
        setchargestr(obj,tstr);
	strcat(Str4,tstr);
	break;
      case MISSILEWEAPON:
      case ARMOR:
      case RING:
      case SHIELD:
      case WEAPON: 
	setplustr(obj,tstr);
	strcat(Str4, tstr);
	break;
      default: strcat(Str4,""); break;
      }
    }
    return(Str4);
  }
}

char *cashstr()
{
  if (difficulty() < 3) return("copper pieces");
  else if (difficulty() < 5) return("silver pieces");
  else if (difficulty() < 7) return("gold pieces");
  else if (difficulty() < 8) return("semiprecious gems");
  else if (difficulty() < 9) return("mithril pieces");
  else if (difficulty() < 10) return("precious gems"); 
  else return("orichalc pieces");
}

/* return an object's plus as a string */
void setplustr(obj,pstr)
pob obj;
char *pstr;
{
  pstr[0] = ' ';
  pstr[1] = (obj->plus < 0 ? '-' : '+' );
  if (abs(obj->plus) < 10) {
    pstr[2] = '0' + abs(obj->plus);
    pstr[3] = 0;
  }
  else {
    pstr[2] = '0' + abs(obj->plus / 10);
    pstr[3] = '0' + abs(obj->plus % 10);
    pstr[4] = 0;
  }
}

/* return an object's number as a string */
void setnumstr(obj,nstr)
pob obj;
char *nstr;
{
  if (obj->number < 2)
    nstr[0] = 0;
  else if (obj->number < 10) {
    nstr[0] = '0' + obj->number;
    nstr[1] = 'x';
    nstr[2] = ' ';
    nstr[3] = 0;
  }
  else if (obj->number < 41) {
    nstr[0] = '0' + ((int)(obj->number / 10));
    nstr[1] = '0' + (obj->number % 10);
    nstr[2] = 'x';
    nstr[3] = ' ';
    nstr[4] = 0;
  }
  else strcpy(nstr,"lots of ");
}




/* return object with charges */
void setchargestr(obj,cstr)
pob obj;
char *cstr;
{
  cstr[0] = ' ';
  cstr[1] = '[';
  if (obj->charge < 0) {
    cstr[2]='d';
    cstr[3]='e';
    cstr[4]='a';
    cstr[5]='d';
    cstr[6]=']';
    cstr[7]=0;
  }
  else if (obj->charge < 10) {
    cstr[2] = '0' + obj->charge;
    cstr[3] = ']';
    cstr[4] = 0;
  }
  else {
    cstr[2] = '0' + ((int)(obj->charge / 10));
    cstr[3] = '0' + (obj->charge % 10);
    cstr[4] = ']';
    cstr[5] = 0;
  }
}


void give_money(m)
struct monster *m;
{
  pob cash;

  cash = detach_money();
  if (cash == NULL)
    setgamestatus(SKIP_MONSTERS);
  else givemonster(m,cash);
}
    
    

void givemonster(m,o)
struct monster *m;
struct object *o;
{
  /* special case -- give gem to LawBringer */
  if ((m->id == ML10+2) && (o->id == ARTIFACTID+21)) {
    clearmsg();
    print1("The LawBringer accepts the gem reverently.");
    print2("He raises it above his head, where it bursts into lambent flame!");
    morewait();
    print1("You are bathed in a shimmering golden light.");
    print2("You feel embedded in an infinite matrix of ordered energy.");
    morewait();
    if (Imprisonment > 0)
      Imprisonment = 0;
    if (Player.rank[ORDER] == -1) {
      print2("You have been forgiven. You feel like a Paladin....");
      Player.rank[ORDER] = 1;
    }
    Player.alignment += 200;
    Player.pow = Player.maxpow = Player.pow * 2;
    gain_experience(2000);
    setgamestatus(GAVE_STARGEM);
  }
  else {
    if (m->uniqueness == COMMON) {
      strcpy(Str3,"The ");
      strcat(Str3,m->monstring);
    }
    else strcpy(Str3,m->monstring);
    
    if (m_statusp(m,GREEDY) || m_statusp(m,NEEDY)) {
      m_pickup(m,o);
      strcat(Str3," takes your gift");
      print1(Str3);
      Player.alignment++;
      if (m_statusp(m,GREEDY) && (true_item_value(o) < (long) m->level*100))
      nprint1("...but does not appear satisfied.");
      else if (m_statusp(m,NEEDY) && 
	       (true_item_value(o) < (long) Level->depth*Level->depth))
	nprint1("...and looks chasteningly at you.");
      else {
	nprint1("...and seems happy with it.");
	m_status_reset(m,HOSTILE);
	m_status_reset(m,GREEDY);
	m_status_reset(m,NEEDY);
      }
    }
    else if (m_statusp(m,HUNGRY)) {
      
      if (((m->id == HORSE) && (o->id == FOODID+15)) || /* grain */
	  ((m->id != HORSE) &&
	   ((o->usef == I_FOOD) || (o->usef == I_POISON_FOOD)))) {
	strcat(Str3," wolfs down your food ... ");
	print1(Str3);
	m_status_reset(m,HUNGRY);
	m_status_reset(m,HOSTILE);
	if  (o->usef == I_POISON_FOOD) {
	  Player.alignment -= 2;
	  nprint1("...and chokes on the poisoned ration!");
	  morewait();
	  m_status_set(m,HOSTILE);
	  m_damage(m,100,POISON);
	}
	else nprint1("...and now seems satiated.");
	morewait();
	free((char *)o);
      }
      else {
	strcat(Str3," spurns your offering and leaves it on the ground.");
	print1(Str3);
	drop_at(m->x,m->y,o);
      }
    }
    else {
      strcat(Str3," doesn't care for your offering and drops it.");
      print1(Str3);
      drop_at(m->x,m->y,o);
    }
  }
}




/* will clear all, not just one of an object. */
void conform_lost_object(obj)
pob obj;
{
  if (obj != NULL) conform_lost_objects(obj->number,obj);
}



/* removes n of object from inventory; frees object if appropriate. */

void dispose_lost_objects(n,obj)
int n;
pob obj;
{
  int i,conformed=FALSE,subtracted=FALSE;

  if (obj == NULL)
    return;
  for(i=0;i<MAXITEMS;i++) 
    if (Player.possessions[i] == obj) {
      if (! subtracted) {
	obj->number -= n;
	subtracted = TRUE;
      }
      if (obj->number < 1) {
	if (!conformed) {
	  conform_unused_object(obj);
	  conformed = TRUE;
	}
	Player.possessions[i] = NULL;
      }
    }
  if (obj->number < 1)
    free((char *) obj);
}




/* removes n of object from inventory without freeing object.
   Removes all instances of pointer (might be 2 handed weapon, etc) */
void conform_lost_objects(n,obj)
int n;
pob obj;
{
  int i,conformed=FALSE,subtracted=FALSE;
  if (obj != NULL) for(i=0;i<MAXITEMS;i++) 
    if (Player.possessions[i] == obj) {
      if (! subtracted) {
	obj->number -= n;
	subtracted = TRUE;
      }
      if (obj->number < 1) {
	if (!conformed) {
	  conform_unused_object(obj);
	  conformed = TRUE;
	}
	Player.possessions[i] = NULL;
      }
    }
}


/* clears unused possession */
void conform_unused_object(obj)
pob obj;
{
  if (obj->used) {
    obj->used = FALSE;
    item_use(obj);
  }
  calc_melee();
}


/* select an item from inventory */
/* if itype is NULL_ITEM, any kind of item is acceptable.
   if itype is CASH, any kind of item or '$' (cash) is acceptable.
   if itype is FOOD, CORPSE or FOOD is acceptable, but only FOOD is
listed in the possibilities.
   if itype is any other object type (eg SCROLL, POTION, etc.), only
that type of item is acceptable or is listed */

int getitem(itype)
short itype;
{
  char invstr[64];
  char index;
  int i,k=0,ok=FALSE,drewmenu=FALSE,found=FALSE;

  found = ((itype == NULL_ITEM) || ((itype == CASH) && (Player.cash > 0)));
  invstr[0]=0;
  for(i=1;i<MAXITEMS;i++)
    if (Player.possessions[i] != NULL)
      if ((itype == NULL_ITEM) ||
	  (itype == CASH) ||
	  (Player.possessions[i]->objchar == itype) ||
	  ((itype == FOOD) && (Player.possessions[i]->objchar == CORPSE))) {
	   found = TRUE;
	   invstr[k++] = 'a'+i-1;
	   invstr[k] = 0;
	 }
  if ((itype == CASH) && found) {
    invstr[k++] = '$';
    invstr[k] = 0;
  }
  if (! found) {
    print3("Nothing appropriate.");
    return(ABORT);
  }
  else {
    print2("Select an item [");
    nprint2(invstr);
    nprint2(",?] ");
    while (! ok) {
      index = (char) mcigetc();
      if (index == '?') {
	drewmenu = TRUE;
	for (i=1;i<MAXITEMS;i++)
	  if (Player.possessions[i] != NULL)
	    if ((itype == NULL_ITEM) || 
		(itype == CASH) ||
		(Player.possessions[i]->objchar == itype) ||
		((itype == FOOD) && 
		 (Player.possessions[i]->objchar == CORPSE)))
	      display_inventory_slot(i,FALSE);
      }
      else if (index == ESCAPE) ok = TRUE;
      else if (index == (CASH&0xff)) {
	if (itype == CASH) ok = TRUE;
	else {
	  print3("You cannot select cash now.");
	  ok = FALSE;
	}
      }
      else if (badobject(index) || (! strmem(index,invstr)))
	print3("Nope! Try again [? for inventory, ESCAPE to quit]:");
      else ok = TRUE;
    }
    if (drewmenu) xredraw();
    if (index == ESCAPE) return(ABORT);
    else if (index == (CASH&0xff)) return(CASHVALUE);
    else return(index+1-'a');
  }
}


/* true if the numerical index based on 'a' == 1 turns out to be either
out of the range of the possessions array or a null item */
int badobject(slotchar)
char slotchar;
{
  int slot = slotchar + 1 - 'a';
  if ((slot<1) || (slot >= MAXITEMS)) return(TRUE);
  else return(Player.possessions[slot] == NULL);
}


#ifndef MSDOS
/* this takes the numerical index directly for the same effect as badobject*/
int baditem(slotnum)
int slotnum;
{
  if ((slotnum<1) || (slotnum >= MAXITEMS)) return(TRUE);
  else return(Player.possessions[slotnum] == NULL);
}
#endif
    


/* formerly add_item_to_pack */
void gain_item(o)
struct object *o;
{
  if (o->uniqueness == UNIQUE_MADE)
    Objects[o->id].uniqueness = UNIQUE_TAKEN;
  if (o->objchar == CASH) {
    print2("You gained some cash.");
    Player.cash += o->basevalue;
    free((char *)o);
    dataprint();
  }
  else if (optionp(PACKADD)) {
    if (! get_to_pack(o)) {
      Player.possessions[O_UP_IN_AIR] = o;
      top_inventory_control();
    }
  }
  else {
    Player.possessions[O_UP_IN_AIR] = o;
    top_inventory_control();
  }
}


/* Adds item to pack list */
void add_to_pack(o)
pob o;
{
  if (Player.packptr >= MAXPACK) {
    print3("Your pack is full. The item drops to the ground.");
    drop_at(Player.x,Player.y,o);
  }
  else {
    Player.pack[Player.packptr++] = o;
    print3("Putting item in pack.");
  }
}


/* Adds item to pack list, maybe going into inventory mode if pack is full */
int get_to_pack(o)
pob o;
{
  if (Player.packptr >= MAXPACK) {
    print3("Your pack is full.");
    morewait();
    return(FALSE);
  }
  else {
    Player.pack[Player.packptr++] = o;
    print3("Putting item in pack.");
    return(TRUE);
  }
}



/* takes something from pack, puts to slot, 
or to 'up-in-air', one of which at least must be empty */
int take_from_pack(slot,display)
int slot,display;
{
  char response;
  int i,quit = FALSE,ok=TRUE,displayed=FALSE;
  pob item;
  if (Player.possessions[slot] != NULL) 
    slot = O_UP_IN_AIR;
  if (Player.possessions[slot] != NULL) 
    print3("slot is not empty!");
  else if (Player.packptr == 0)
    print3("Pack is empty!");
  else {
    do {
      ok = TRUE;
      print1("Enter pack slot letter, or ? to show pack, or ESCAPE to quit.");
      response = mgetc();
      if (response == '?') {
	display_pack();
	displayed = TRUE;
	ok = FALSE;
      }
      else if (response == ESCAPE) quit = TRUE;
      else{
	ok = ((response >= 'A') && (response < 'A'+Player.packptr));
	if (ok) ok = slottable(Player.pack[response-'A'],slot);
      }
    } while (! ok);
    if (! quit) {
      if (response - 'A' > 10) {
	print1("You begin to rummage through your pack.");
	morewait();
	Command_Duration += 10;
      }
      if (response - 'A' > 5) {
	print1("You search your pack for the item.");
	Command_Duration += 5;
	morewait();
      }
      print1("You take the item from your pack.");
      morewait();
      Command_Duration += 2;
      item = Player.possessions[slot] = Player.pack[response-'A'];
      for(i=response-'A';i<Player.packptr-1;i++)
	Player.pack[i] = Player.pack[i+1];
      Player.pack[--Player.packptr] = NULL;
      
      if ((slot == O_READY_HAND || slot == O_WEAPON_HAND) &&
        twohandedp(item->id))
      {
        if (Player.possessions[O_READY_HAND] == NULL)
          Player.possessions[O_READY_HAND] = item;
        if (Player.possessions[O_WEAPON_HAND] == NULL)
          Player.possessions[O_WEAPON_HAND] = item;
      }
      if (item_useable(item,slot)) {
	item->used = TRUE;
	item_use(item);
	morewait();
	if (item->number > 1) pack_extra_items(item);
      }
    }
  }
  if (displayed) {
    if (display)
      display_possessions();
    else
      xredraw();
  }
  return slot;
}




#ifndef MSDOS
/* General interface to inventory */
void item_inventory(topline)
int topline;
{
  if (topline) {
    display_possessions();
    inventory_control();
  }
  else top_inventory_control();
}
#endif





/* inventory_control assumes a few setup things have been done,
   like displaying the slots, loading the O_UP_IN_AIR item, etc.

   Each action uses up a little time. If only inspection actions
   are taken, no time is used up. */


void inventory_control()
{
  int slot = 0,done=FALSE;
  int response;
  char letter;
#ifdef MSDOS
  int simple = 0;
#endif
  clearmsg3();
  checkclear();
  print1("Action [d,e,l,p,s,t,x,>,<,?,ESCAPE]:");
  show_inventory_slot(slot,FALSE);
  display_inventory_slot(O_UP_IN_AIR,FALSE);
  do {
    move_slot(slot,slot,MAXITEMS);
    response = mcigetc();

    switch(response) {
    case 12:
    case 18: /* ^l, ^r */
      display_possessions();
      break;
    case 'd':
      if (Player.possessions[O_UP_IN_AIR] != NULL) 
      {
	drop_from_slot(O_UP_IN_AIR);
	display_inventory_slot(O_UP_IN_AIR, FALSE);
      }
      else if (Player.possessions[slot] != NULL) 
      {
	drop_from_slot(slot);
	show_inventory_slot(slot, FALSE);
      }
      else print3("Nothing in selected slot!");
      Command_Duration++;
      break; 
    case 'l':
      Str1[0] = '\0';
      if (Player.possessions[slot] != NULL) {
	if (!strcmp(itemid(Player.possessions[slot]),
	  Player.possessions[slot]->objstr))
	  print3("You notice nothing new about it.");
	else {
	  if (Player.possessions[slot]->uniqueness == COMMON)
	    strcat(Str1, "Your ");
	  strcat(Str1, itemid(Player.possessions[slot]));
	  if (Player.possessions[slot]->objchar == BOOTS)
	    strcat(Str1, " look like ");
	  else {
	    strcat(Str1, " looks like a");
	    letter = Player.possessions[slot]->objstr[0];
	    if (letter == 'a' || letter == 'A' || letter == 'e' ||
	      letter == 'E' || letter == 'i' || letter == 'I' ||
	      letter == 'o' || letter == 'O' || letter == 'u' || letter == 'U')
	      strcat(Str1, "n ");
	    else
	      strcat(Str1, " ");
	  }
	  strcat(Str1, Player.possessions[slot]->objstr);
	  print3(Str1);
	}
      }
      else print3("Nothing in selected slot!");
      break;
    case 'p': 
      if (Player.possessions[slot] != NULL) 
      {
	put_to_pack(slot);
	show_inventory_slot(slot, FALSE);
      }
      Command_Duration+=5;
      break;
    case 's':
      display_pack();
      morewait();
      display_possessions();
      Command_Duration+=5;
      break;
    case 't': 
      show_inventory_slot(take_from_pack(slot,TRUE), FALSE);
      Command_Duration+=5;
      break;
    case 'e':
      switch_to_slot(slot);
      show_inventory_slot(O_UP_IN_AIR,FALSE);
      show_inventory_slot(slot,FALSE);
      Command_Duration+=2;
      break;
    case 'x':
      switch_to_slot(slot);
      show_inventory_slot(O_UP_IN_AIR,FALSE);
      show_inventory_slot(slot,FALSE);
      Command_Duration+=2;
      done = (Player.possessions[O_UP_IN_AIR] == NULL);
      break;
    case 'j':
    case '>':
#if defined(KEY_DOWN) && defined(MSDOS)
    case KEY_DOWN:
      simple = 1;
#endif
      slot = move_slot(slot,slot+1,MAXITEMS);
      break;
    case 'k':
    case '<':
#if defined(KEY_UP) && defined(MSDOS)
    case KEY_UP:
      simple = 1;
#endif
      slot = move_slot(slot,slot-1,MAXITEMS);
      break;
#ifdef KEY_HOME
    case KEY_HOME:
      slot = move_slot(slot,0,MAXITEMS);
      break;
#endif
#ifdef KEY_LL
    case KEY_LL:
      slot = move_slot(slot,MAXITEMS-1,MAXITEMS);
      break;
#endif
    case '?':
      menuclear();
      menuprint("d:\tDrop up-in-air or current item\n");
      menuprint("e:\tExchange current slot with up-in-air slot\n");
      menuprint("l:\tLook at current item\n");
      menuprint("p:\tPut up-in-air or current item in pack\n");
      menuprint("s:\tShow contents of pack\n");
      menuprint("t:\tTake something from pack into the\n\tcurrent or up-in-air slot\n");
      menuprint("x:\tAs 'e', but exit if up-in-air slot finishes empty\n");
      menuprint(">:\tMove down one slot/item\n");
      menuprint("<:\tMove up one slot/item\n");
      menuprint("?:\tDisplay help (this message + help file)\n");
      menuprint("ESCAPE:\texit\n");
      showmenu();
      clearmsg();
      print1("Display full help? (y/n)");
      if (ynq1() == 'y')
	  inv_help();
      display_possessions();
      break;
    case ESCAPE:
      if (Player.possessions[O_UP_IN_AIR] != NULL) {
	drop_at(Player.x,Player.y,Player.possessions[O_UP_IN_AIR]);
	Player.possessions[O_UP_IN_AIR] = NULL;
	print3("Object 'up in air' dropped.");
      }
      done = TRUE;
      break;
    }
#ifndef MSDOS
    calc_melee();
#else
    if (!simple)
      calc_melee();
    else
      simple = 0;
#endif
  } while (! done);
  xredraw();
}







/* same as inventory_control, but only uses msg window for i/o*/


void top_inventory_control()
{
  int slot = 0,done=FALSE,usedmenu=FALSE;
  char response, letter;
  clearmsg3();
  do {
    clearmsg1();
    print1("Action [d,e,l,p,s,t,x,~,?,ESCAPE]:");
    print2("'Up in air': ");
    if (Player.possessions[O_UP_IN_AIR] == NULL) nprint2("NOTHING");
    else nprint2(itemid(Player.possessions[O_UP_IN_AIR]));
    response = (char) mcigetc();

    switch(response) {
    case 'd':
      if (Player.possessions[O_UP_IN_AIR] != NULL) 
	drop_from_slot(O_UP_IN_AIR);
      else {
	slot = get_inventory_slot();
	if (Player.possessions[slot] != NULL) 
	  drop_from_slot(slot);
	else print3("Nothing in selected slot!");
      }
      Command_Duration++;
      break; 
    case 'l':
      Str1[0] = '\0';
      slot = get_inventory_slot();
      if (Player.possessions[slot] != NULL) {
	if (!strcmp(itemid(Player.possessions[slot]),
	  Player.possessions[slot]->objstr))
	  print3("You notice nothing new about it.");
	else {
	  if (Player.possessions[slot]->uniqueness == COMMON)
	    strcat(Str1, "Your ");
	  strcat(Str1, itemid(Player.possessions[slot]));
	  if (Player.possessions[slot]->objchar == BOOTS)
	    strcat(Str1, " look like ");
	  else {
	    strcat(Str1, " looks like a");
	    letter = Player.possessions[slot]->objstr[0];
	    if (letter == 'a' || letter == 'A' || letter == 'e' ||
	      letter == 'E' || letter == 'i' || letter == 'I' ||
	      letter == 'o' || letter == 'O' || letter == 'u' || letter == 'U')
	      strcat(Str1, "n ");
	    else
	      strcat(Str1, " ");
	  }
	  strcat(Str1, Player.possessions[slot]->objstr);
	  print3(Str1);
	}
      }
      else print3("Nothing in selected slot!");
      break;
    case 'p': 
      if (Player.possessions[O_UP_IN_AIR] == NULL)
	slot = get_inventory_slot();
      else slot = O_UP_IN_AIR;
      put_to_pack(slot); 
      Command_Duration+=5;
      break;
    case 's':
      display_pack();
      usedmenu = TRUE;      
      Command_Duration+=5;
      break;
    case 't': 
      slot = get_inventory_slot();
      (void) take_from_pack(slot,FALSE); 
      Command_Duration+=5;
      break;
    case 'e':
      slot = get_inventory_slot();
      switch_to_slot(slot);
      Command_Duration+=2;
      break;
    case 'x':
      slot = get_inventory_slot();
      switch_to_slot(slot);
      Command_Duration+=2;
      done = (Player.possessions[O_UP_IN_AIR] == NULL);
      break;
    case '~':
      display_possessions();
      inventory_control();
      usedmenu = TRUE;
      done = TRUE;
      break;
    case '?':
      menuclear();
      menuprint("d:\tDrop an item\n");
      menuprint("e:\tExchange a slot with up-in-air slot\n");
      menuprint("l:\tLook at an item\n");
      menuprint("p:\tPut an item in pack\n");
      menuprint("s:\tShow contents of pack\n");
      menuprint("t:\tTake something from pack into a slot\n");
      menuprint("x:\tAs 'e', above, exit if up-in-air slot finishes empty\n");
      menuprint("~:\tEnter full-screen inventory mode\n");
      menuprint("?:\tDisplay help (this message + help file)\n");
      menuprint("ESCAPE:\texit\n");
      showmenu();
      clearmsg();
      print1("Display full help? (y/n)");
      if (ynq1() == 'y')
	  inv_help();
      usedmenu=TRUE;
      break;
    case ESCAPE:
      if (Player.possessions[O_UP_IN_AIR] != NULL) {
	drop_at(Player.x,Player.y,Player.possessions[O_UP_IN_AIR]);
	Player.possessions[O_UP_IN_AIR] = NULL;
	print3("Object 'up in air' dropped.");
      }
      done = TRUE;
      break;
    }
    calc_melee();
  } while (! done);
  if (usedmenu) 
    xredraw();
}





int get_inventory_slot()
{
  int ok;
  char response;
  do {
    clearmsg1();
    print1("Which inventory slot [a..o,*='up-in-air' slot]?");
    response = (char) mcigetc(); 
    ok = ((response == '*') ||
	  ((response >= 'a') && (response < 'a' + MAXITEMS - 1)));
  } while (! ok);
  return((response == '*') ? 0 : ((int) (response + 1 - 'a')));
}
    


/* returns some number between 0 and o->number */
int get_item_number(o)
pob o;
{
  int n=0;
  if (o->number == 1)
    return 1;
  do {
    clearmsg();
    print1("How many? -- max ");
    mnumprint(o->number);
    nprint1(" :");
    n = (int) parsenum();
    if (n>o->number) print3("Too many!");
    else if (n<1) n = 0;
  } while (n > o->number);
  if (n < 1) n = 0;
  return(n);
}
  
void drop_from_slot(slot)
int slot;
{
  int n,waitflag;
  if (Player.possessions[slot] != NULL) {
    if(cursed(Player.possessions[slot]) == TRUE + TRUE)
      print3("It sticks to your fingers!");
    else {
      n = get_item_number(Player.possessions[slot]);
      if (n > 0) {
	p_drop_at(Player.x,Player.y,n,Player.possessions[slot]);
	waitflag = (Player.possessions[slot]->used && 
		    (Player.possessions[slot]->number == n));
	conform_lost_objects(n,Player.possessions[slot]);
	if (waitflag) morewait();
      }
      else print3("Didn't drop anything.");
    }
  }
  else print3("Didn't drop anything.");
}


void put_to_pack(slot)
int slot;
{
  int waitflag,num = 1;
  pob temp,oslot = Player.possessions[slot];
  if (oslot == NULL) 
    print3("Slot is empty!");
  else if (cursed(oslot) == TRUE+TRUE)
    print3("Item is cursed!");
  else {
    num = get_item_number(oslot);
    if (num > 0) {
      temp = split_item(num,oslot);
      waitflag = (oslot->used && (oslot->number == num));
      conform_lost_objects(num,oslot);
      if (waitflag) morewait();
      add_to_pack(temp);
    }
  }
}


/* splits num off of item to make newitem which is returned */
/* something else (conform_lost_objects) has to reduce the actual
   number value of item and Player.itemweight */
pob split_item(num,item)
int num;
pob item;
{
  pob newitem=NULL;
  if (item != NULL) {
    newitem = ((pob) checkmalloc(sizeof(objtype)));
    *newitem = *item;
    if (num <= item->number)
      newitem->number = num;
    /* else num > item->number, so return newitem with number = item->number */
    newitem->used = FALSE;	/* whether the original item was used or not */
  }
  return(newitem);
}



/* Trades contents of "up in air" slot with selected slot. One or both
may be null. If both slots are 'objequal' merges two groups into one
in the selected slot. If one slot is null and the number of the other
is greater than one, requests how many to move. */

void switch_to_slot(slot)
int slot;
{
  pob oslot = Player.possessions[slot];
  pob oair = Player.possessions[O_UP_IN_AIR];
  pob otemp = NULL;
  int slotnull,airnull,num=1,trade=FALSE,put=FALSE,take=FALSE,merge=FALSE;
  int s2h=FALSE,a2h=FALSE;

  /* ie, is cursed and in use */
  if (slot == O_UP_IN_AIR)
    print3("This action makes no sense!");
  else if (cursed(oslot)==TRUE+TRUE)
    print3("The object in that slot is cursed -- you can't get rid of it!");
  else {

    slotnull = (oslot == NULL); 
    airnull = (oair == NULL);

    if  (!slotnull)
      s2h = (Player.possessions[O_READY_HAND] ==
	     Player.possessions[O_WEAPON_HAND]);
    
    if (! airnull)
      a2h = (twohandedp(oair->id) && 
	     ((slot == O_READY_HAND) || (slot == O_WEAPON_HAND)));
    
    
    /* figure out which action to take */

    /* merge if both are same kind of object */
    merge = objequal(oslot,oair);

    take = ((!merge) && (!slotnull) && airnull);
    
    put = ((!merge) && slotnull && (!airnull) && slottable(oair,slot));
    
    trade = ((!merge) && (!slotnull) && (!airnull) && slottable(oair,slot));

    if (merge) merge_item(slot);

    else if (put) {

      /* deal with a 2-handed weapon */
      if (a2h) {
	if (Player.possessions[O_READY_HAND] == NULL)
	  Player.possessions[O_READY_HAND] = oair;
	if (Player.possessions[O_WEAPON_HAND] == NULL)
	  Player.possessions[O_WEAPON_HAND] = oair;
      }
      else Player.possessions[slot] = oair;
      Player.possessions[O_UP_IN_AIR] = NULL;
      if (item_useable(oair,slot)) {
	oair->used = TRUE;
	item_use(oair);
	morewait();
	if (oair->number > 1) pack_extra_items(oair);
      }
      Player.possessions[O_UP_IN_AIR] = NULL;
    }

    else if (take) {
      num = get_item_number(oslot);
      if (num > 0) {
	otemp = split_item(num,oslot);
	dispose_lost_objects(num,oslot);
	Player.possessions[O_UP_IN_AIR] = otemp;
      }
      if (s2h) {
        if (Player.possessions[O_READY_HAND] == oslot)
          Player.possessions[O_READY_HAND] = NULL;
        if (Player.possessions[O_WEAPON_HAND] == oslot)
          Player.possessions[O_WEAPON_HAND] = NULL;
      }
    }

    else if (trade) {
      
      /* first remove item from slot */
      num = oslot->number;
      conform_lost_objects(oslot->number,oslot);
      oslot->number = num;
      
      Player.possessions[O_UP_IN_AIR] = oslot;

      Player.possessions[slot] = oair;

      if (s2h) {
        if (Player.possessions[O_READY_HAND] == oslot)
          Player.possessions[O_READY_HAND] = NULL;
        if (Player.possessions[O_WEAPON_HAND] == oslot)
          Player.possessions[O_WEAPON_HAND] = NULL;
      }
      
      if (a2h) {
	if (Player.possessions[O_READY_HAND] == NULL)
	  Player.possessions[O_READY_HAND] = oair;
	if (Player.possessions[O_WEAPON_HAND] == NULL)
	  Player.possessions[O_WEAPON_HAND] = oair;
      }
      
      if (item_useable(oair,slot)) {
	oair->used = TRUE;
	item_use(oair);
	morewait();
	if (oair->number > 1) pack_extra_items(oair);
      }
    }
  }
}




/* merges the up-in-air items into the selected items */

void merge_item(slot)
int slot;
{
  Player.possessions[slot]->number +=
    Player.possessions[O_UP_IN_AIR]->number;
  Player.possessions[O_UP_IN_AIR] = NULL;
}


/* are two objects equal except for their number field? */
/* returns false if either object is null */
int objequal(o,p)
struct object *o,*p;
{
  if ((o == NULL) || (p == NULL)) return(FALSE);
  else return(
	 (o->id == p->id) &&
	 (o->plus == p->plus) &&
	 (o->charge == 0) &&
	 (p->charge == 0) &&
	 (o->dmg == p->dmg) &&
	 (o->hit == p->hit) &&
	 (o->aux == p->aux) &&
	 (o->known == p->known) &&
	 (o->blessing == p->blessing) &&
	 (o->usef == p->usef)
	 );

}

/* criteria for being able to put some item in some slot */
int slottable(o,slot)
pob o;
int slot;
{
  int ok = TRUE;
  if (o == NULL) ok = FALSE;
  else if (slot == O_ARMOR) {
    if (o->objchar != ARMOR) {
      print3("Only armor can go in the armor slot!");
      ok = FALSE;
    }
  }
  else if (slot == O_SHIELD) {
    if (o->objchar != SHIELD) {
      print3("Only a shield can go in the shield slot!");
      ok = FALSE;
    }
  }
  else if (slot == O_BOOTS) {
    if (o->objchar != BOOTS) {
      print3("Only boots can go in the boots slot!");
      ok = FALSE;
    }
  }
  else if (slot == O_CLOAK) {
    if (o->objchar != CLOAK) {
      print3("Only a cloak can go in the cloak slot!");
      ok = FALSE;
    }
  }
  else if (slot >= O_RING1) {
    if (o->objchar != RING) {
      print3("Only a ring can go in a ring slot!");
      ok = FALSE;
    }
  }
  return(ok);
}



/* whether or not an item o can be used in a slot. Assumes o can in
   fact be placed in the slot. */
int item_useable(o,slot)
pob o;
int slot;
{
  /* don't have to check the object in the first if since only armor
  can go in armor slot, cloak in cloak slot, etc */

  if ((slot == O_ARMOR) || 
      (slot == O_CLOAK) ||
      (slot == O_SHIELD) ||
      (slot == O_BOOTS) ||
      (slot >= O_RING1))
    return(TRUE);

  /* weapon is useable if it is put in weapon hand or if it is two-handed
     and put in either hand when the other also holds the weapon */

  else if ((o->objchar == WEAPON) ||
	   (o->objchar == MISSILEWEAPON)) {
    if (twohandedp(o->id) && 
	((slot==O_READY_HAND)||(slot==O_WEAPON_HAND))) {
      if (Player.possessions[O_READY_HAND] == 
	  Player.possessions[O_WEAPON_HAND]) {
	print1("You heft the weapon and find you must use both hands.");
	morewait();
	return(TRUE);
      }
      else {
	print1("This weapon is two-handed, so at the moment, ");
	print2("you are just lugging it around....");
	morewait();
	return(FALSE);
      }	
    }
    else return(slot == O_WEAPON_HAND);
  }
  else return(FALSE);
}








/* returns FALSE if not cursed, TRUE if cursed but not used,
   TRUE + TRUE if cursed and used */
int cursed(obj)
pob obj;
{
  return((obj == NULL) ? 
	 FALSE : 
	 ((obj->blessing < 0) ? 
	  (obj->used == TRUE) + TRUE : 
	  FALSE));
}




/* returns true if item with id and charge is found in pack or in
   inventory slot. charge is used to differentiate
   corpses instead of aux, which is their food value. */
int find_item(o,id,chargeval)
int id,chargeval;
pob *o;
{
  int i,found=FALSE;
  *o=NULL;
  for(i=1;((i<MAXITEMS)&&(! found));i++)
    if (Player.possessions[i] != NULL)
      if ((Player.possessions[i]->id == id) &&
	  ((chargeval == -1) ||
	   (Player.possessions[i]->charge == chargeval))) {
	*o = Player.possessions[i];
	found = TRUE;
      }
  if (! found)
    for(i=0;((i<Player.packptr)&&(! found));i++)
      if (Player.pack[i] != NULL)
	if ((Player.pack[i]->id == id) &&
	    ((chargeval == -1) ||
	     (Player.pack[i]->charge == chargeval))) {
	  *o = Player.pack[i];
	  found = TRUE;
	}
  return(found);
}



/* returns true if item with id and charge is found in pack or in
   inventory slot. Destroys item. charge is used to differentiate
   corpses instead of aux, which is their food value. */
int find_and_remove_item(id,chargeval)
int id,chargeval;
{
  int i,found=FALSE;
  pob o=NULL;

  for(i=1;((i<MAXITEMS)&&(! found));i++)
    if (Player.possessions[i] != NULL)
      if ((Player.possessions[i]->id == id) &&
	  ((chargeval == -1) ||
	   (Player.possessions[i]->charge == chargeval))) {
	o = Player.possessions[i];
	conform_lost_objects(1, o);
	found = TRUE;
      }
  if (! found) for(i=0;((i<Player.packptr)&&(! found));i++)
    if (Player.pack[i] != NULL)
      if ((Player.pack[i]->id == id) &&
	  ((chargeval == -1) ||
	   (Player.pack[i]->charge == chargeval))) {
	Player.pack[i]->number--;
	if (Player.pack[i]->number == 0) {
	  free((char *)Player.pack[i]);
	  Player.pack[i] = NULL;
	}
	found = TRUE;
      }
  fixpack();
  return(found);
}




void lose_all_items()
{
  int i;
  print1("You notice that you are completely devoid of all possessions.");
  morewait();
  for(i=0;i<MAXITEMS;i++)
    if (Player.possessions[i] != NULL) {
      dispose_lost_objects(Player.possessions[i]->number,
			   Player.possessions[i]);
      Player.possessions[i] = NULL;
    }
  for(i=0;i<MAXPACK;i++) {
    if (Player.pack[i] != NULL) 
      free((char *) Player.pack[i]);
    Player.pack[i] = NULL;
  }
  Player.packptr = 0;
  calc_melee();
  morewait();
}


/* prevents people from wielding 3 short swords, etc. */
void pack_extra_items(item)
pob item;
{
  pob extra=((pob) checkmalloc(sizeof(objtype)));
  *extra = *item;
  extra->number = item->number-1;
  extra->used = FALSE;
  item->number = 1;
  if (Player.packptr < MAXPACK) {
    print3("Putting extra items back in pack.");
    morewait();
    Player.pack[Player.packptr++] = extra;
  }
  else if (Player.possessions[O_UP_IN_AIR] == NULL) {
    print3("Extra copies of item are 'up in the air'");
    Player.possessions[O_UP_IN_AIR] = extra;
  }
  else {
    print3("No room for extra copies of item -- dropping them.");
    drop_at(Player.x,Player.y,extra);
  }
  calc_melee();
}


/* makes sure Player.pack is OK, (used after sale from pack) */
void fixpack()
{
  pob tpack[MAXPACK];
  int i,tctr=0;
  for(i=0;i<MAXPACK;i++) tpack[i] = NULL;
  for(i=0;i<MAXPACK;i++)
    if (Player.pack[i]!=NULL) 
      tpack[tctr++] = Player.pack[i];
  for(i=0;i<MAXPACK;i++)
    Player.pack[i]=tpack[i];
  Player.packptr = tctr;
}


/* show slots, with appropriate additional displays if two-handed weapons */
/* are involved */
void show_inventory_slot(slotnum,topline)
int slotnum;
int topline;
{
  if (!topline)
    if (Player.possessions[O_READY_HAND] == Player.possessions[O_WEAPON_HAND] &&
      (slotnum == O_READY_HAND || slotnum == O_WEAPON_HAND))
    {
      display_inventory_slot(O_READY_HAND, topline);
      display_inventory_slot(O_WEAPON_HAND, topline);
    }
    else if (slotnum == O_UP_IN_AIR && Player.possessions[O_UP_IN_AIR] &&
      twohandedp(Player.possessions[O_UP_IN_AIR]->id))
    {
      display_inventory_slot(O_READY_HAND, topline);
      display_inventory_slot(O_WEAPON_HAND, topline);
      display_inventory_slot(slotnum, topline);
    }
    else
      display_inventory_slot(slotnum, topline);
  else
    display_inventory_slot(slotnum, topline);
}
